
// 正则表达式(regular expression)描述了一种字符串匹配的模式（pattern）,
// 可以用来检查一个串是否含有某种子串、将匹配的子串替换或者从某个串中取出符合某个条件的子串等.
/*
通过使用正则表达式,可以：
1. 测试字符串内的模式.
例如,可以测试输入字符串,以查看字符串内是否出现电话号码模式或信用卡号码模式.这称为数据验证.
2. 替换文本.
可以使用正则表达式来识别文档中的特定文本,完全删除该文本或者用其他文本替换它.
3. 基于模式匹配从字符串中提取子字符串.
可以查找文档内或输入域内特定的文本.
*/
// 正则表达式后面的全局标记 g 指定将该表达式应用到输入字符串中能够查找到的尽可能多的匹配.
// 表达式的结尾处的不区分大小写 i 标记指定不区分大小写.
var testStr = "abc123def456abc";
var patt = /[0-9]+/g;
console.log(testStr.match(patt));
var patt = /[0-9]+abc$/g;
console.log(testStr.match(patt));
/*
^ 为匹配输入字符串的开始位置.
[0-9]+匹配多个数字, [0-9] 匹配单个数字,+ 匹配一个或者多个.
abc$匹配字母 abc 并以 abc 结尾,$ 为匹配输入字符串的结束位置.
*/

/* 普通字符
普通字符包括没有显式指定为元字符的所有可打印和不可打印字符.
这包括所有大写和小写字母、所有数字、所有标点符号和一些其他符号.
*/
/* 非打印字符
非打印字符也可以是正则表达式的组成部分.
\cx	匹配由x指明的控制字符.例如, \cM 匹配一个 Control-M 或回车符.
    x 的值必须为 A-Z 或 a-z 之一.否则,将 c 视为一个原义的 'c' 字符.
\f	匹配一个换页符.等价于 \x0c 和 \cL.
\n	匹配一个换行符.等价于 \x0a 和 \cJ.
\r	匹配一个回车符.等价于 \x0d 和 \cM.
\s	匹配任何空白字符,包括空格、制表符、换页符等等.等价于 [ \f\n\r\t\v].
\S	匹配任何非空白字符.等价于 [^ \f\n\r\t\v].
\t	匹配一个制表符.等价于 \x09 和 \cI.
\v	匹配一个垂直制表符.等价于 \x0b 和 \cK.
*/
/* 特殊字符
匹配这些特殊字符,必须先使用反斜杠字符\ 进行字符"转义".
$	匹配输入字符串的结尾位置.如果设置了 RegExp 对象的 Multiline 属性,则 $ 也匹配 '\n' 或 '\r'.
( )	标记一个子表达式的开始和结束位置.子表达式可以获取供以后使用.
*	匹配前面的子表达式零次或多次.
+	匹配前面的子表达式一次或多次.
.	匹配除换行符 \n 之外的任何单字符.
[	标记一个中括号表达式的开始.
?	匹配前面的子表达式零次或一次,或指明一个非贪婪限定符.
\	将下一个字符标记为或特殊字符、或原义字符、或向后引用、或八进制转义符.
^	匹配输入字符串的开始位置.在方括号表达式中使用时,表示不接受该字符集合.
{	标记限定符表达式的开始.
|	指明两项之间的一个选择.
*/
/* 限定符
限定符用来指定正则表达式的一个给定组件必须要出现多少次才能满足匹配.
*	    匹配前面的子表达式零次或多次.例如,zo* 能匹配 "z" 以及 "zoo".* 等价于{0,}.
+	    匹配前面的子表达式一次或多次.例如,'zo+' 能匹配 "zo" 以及 "zoo",但不能匹配 "z".+ 等价于 {1,}.
?	    匹配前面的子表达式零次或一次.例如,"do(es)?" 可以匹配 "do" 或 "does" 中的"do" .? 等价于 {0,1}.
{n}	    n 是一个非负整数.匹配确定的 n 次.例如,'o{2}' 不能匹配 "Bob" 中的 'o',但是能匹配 "food" 中的两个 o.
{n,}	n 是一个非负整数.至少匹配n 次.例如,'o{2,}' 不能匹配 "Bob" 中的 'o',但能匹配 "foooood" 中的所有 o.
        'o{1,}' 等价于 'o+'.'o{0,}' 则等价于 'o*'.
{n,m}	m 和 n 均为非负整数,其中n <= m.最少匹配 n 次且最多匹配 m 次.例如,"o{1,3}" 将匹配 "fooooood" 中的前三个 o.
        'o{0,1}' 等价于 'o?'.请注意在逗号和两个数之间不能有空格.
*/
// 通过在 *、+ 或 ? 限定符之后放置 ?,该表达式从"贪心"表达式转换为"非贪心"表达式或者最小匹配,一般为匹配一次.
/* 定位符
定位符用来描述字符串或单词的边界.
^	匹配输入字符串开始的位置.如果设置了 RegExp 对象的 Multiline 属性,^ 还会与 \n 或 \r 之后的位置匹配.
$	匹配输入字符串结尾的位置.如果设置了 RegExp 对象的 Multiline 属性,$ 还会与 \n 或 \r 之前的位置匹配.
\b	匹配一个字边界,即字与空格间的位置.
\B	非字边界匹配.
*/
// 匹配章节标题
testStr = "Chapter 123"
patt = /^Chapter [1-9][0-9]*$/g
console.log(testStr.match(patt));

/* 选择
用圆括号将所有选择项括起来,相邻的选择项之间用|分隔.
但用圆括号会有一个副作用,是相关的匹配会被缓存,此时可用非捕获元?:放在第一个选项前来消除这种副作用.
还有两个非捕获元是 ?= 和 ?!,前者为正向预查,在任何开始匹配圆括号内的正则表达式模式的位置来匹配搜索字符串,
后者为负向预查,在任何开始不匹配该正则表达式模式的位置来匹配搜索字符串.
*/
/* 反向引用
对一个正则表达式模式或部分模式两边添加圆括号将导致相关匹配存储到一个临时缓冲区中.
所捕获的每个子匹配都按照在正则表达式模式中从左到右出现的顺序存储.
缓冲区编号从 1 开始,最多可存储 99 个捕获的子表达式.
每个缓冲区都可以使用 \n 访问,其中 n 为一个标识特定缓冲区的一位或两位十进制数.
可以使用非捕获元字符 ?:、?= 或 ?! 来重写捕获,忽略对相关匹配的保存.
*/
// 匹配连续相同单词
testStr = "Is is the cost of of gasoline going up up";
patt = /\b([a-z]+) \1\b/g;
console.log(testStr.match(patt));

// 匹配带协议的网址
testStr = "http://haydnliao.win/graduation-photos/index.html";
patt = /(\w+):\/\/([^/:]+)(:\d*)?([^# ]*)/g;
console.log(testStr.match(patt));


/* 元字符
\	将下一个字符标记为一个特殊字符、或一个原义字符、或一个向后引用、或一个八进制转义符.
    例如,'n' 匹配字符 "n".'\n' 匹配一个换行符.序列 '\\' 匹配 "\" 而 "\(" 则匹配 "(".
^   匹配输入字符串的开始位置.如果设置了 RegExp 对象的 Multiline 属性,^ 也匹配 '\n' 或 '\r' 之后的位置.
$	匹配输入字符串的结束位置.如果设置了RegExp 对象的 Multiline 属性,$ 也匹配 '\n' 或 '\r' 之前的位置.
*	匹配前面的子表达式零次或多次.例如,zo* 能匹配 "z" 以及 "zoo".* 等价于{0,}.
+	匹配前面的子表达式一次或多次.例如,'zo+' 能匹配 "zo" 以及 "zoo",但不能匹配 "z".+ 等价于 {1,}.
?	匹配前面的子表达式零次或一次.例如,"do(es)?" 可以匹配 "do" 或 "does" 中的"do" .? 等价于 {0,1}.
{n}	    n 是一个非负整数.匹配确定的 n 次.例如,'o{2}' 不能匹配 "Bob" 中的 'o',但是能匹配 "food" 中的两个 o.
{n,}    n 是一个非负整数.至少匹配n 次.例如,'o{2,}' 不能匹配 "Bob" 中的 'o',但能匹配 "foooood" 中的所有 o.
        'o{1,}' 等价于 'o+'.'o{0,}' 则等价于 'o*'.
{n,m}	m 和 n 均为非负整数,其中n <= m.最少匹配 n 次且最多匹配 m 次.
        例如,"o{1,3}" 将匹配 "fooooood" 中的前三个 o.'o{0,1}' 等价于 'o?'.请注意在逗号和两个数之间不能有空格.
?	当该字符紧跟在任何一个其他限制符 (*, +, ?, {n}, {n,}, {n,m}) 后面时,匹配模式是非贪婪的.
    非贪婪模式尽可能少的匹配所搜索的字符串,而默认的贪婪模式则尽可能多的匹配所搜索的字符串.
    例如,对于字符串 "oooo",'o+?' 将匹配单个 "o",而 'o+' 将匹配所有 'o'.
.	匹配除 "\n" 之外的任何单个字符.要匹配包括 '\n' 在内的任何字符,请使用像"(.|\n)"的模式.
(?:pattern)	匹配 pattern 但不获取匹配结果,也就是说这是一个非获取匹配,不进行存储供以后使用.
            这在使用 "或" 字符 (|) 来组合一个模式的各个部分是很有用.
            例如, 'industr(?:y|ies) 就是一个比 'industry|industries' 更简略的表达式.
(?=pattern)	正向预查,在任何匹配 pattern 的字符串开始处匹配查找字符串.
            这是一个非获取匹配,也就是说,该匹配不需要获取供以后使用.
            例如,'Windows (?=95|98|NT|2000)' 能匹配 "Windows 2000" 中的 "Windows" ,但不能匹配 "Windows 3.1" 中的 "Windows".
            预查不消耗字符,也就是说,在一个匹配发生后,在最后一次匹配之后立即开始下一次匹配的搜索,而不是从包含预查的字符之后开始.
(?!pattern)	负向预查,在任何不匹配 pattern 的字符串开始处匹配查找字符串.
            这是一个非获取匹配,也就是说,该匹配不需要获取供以后使用.
            例如'Windows (?!95|98|NT|2000)' 能匹配 "Windows 3.1" 中的 "Windows",但不能匹配 "Windows 2000" 中的 "Windows".
            预查不消耗字符,也就是说,在一个匹配发生后,在最后一次匹配之后立即开始下一次匹配的搜索,而不是从包含预查的字符之后开始.
x|y	    匹配 x 或 y.例如,'z|food' 能匹配 "z" 或 "food".'(z|f)ood' 则匹配 "zood" 或 "food".
[xyz]	字符集合.匹配所包含的任意一个字符.例如, '[abc]' 可以匹配 "plain" 中的 'a'.
[^xyz]	负值字符集合.匹配未包含的任意字符.例如, '[^abc]' 可以匹配 "plain" 中的'p'、'l'、'i'、'n'.
[a-z]	字符范围.匹配指定范围内的任意字符.例如,'[a-z]' 可以匹配 'a' 到 'z' 范围内的任意小写字母字符.
[^a-z]	负值字符范围.匹配任何不在指定范围内的任意字符.例如,'[^a-z]' 可以匹配任何不在 'a' 到 'z' 范围内的任意字符.
\b	匹配一个单词边界,也就是指单词和空格间的位置.例如, 'er\b' 可以匹配"never" 中的 'er',但不能匹配 "verb" 中的 'er'.
\B	匹配非单词边界.'er\B' 能匹配 "verb" 中的 'er',但不能匹配 "never" 中的 'er'.
\cx	匹配由 x 指明的控制字符.例如, \cM 匹配一个 Control-M 或回车符.x 的值必须为 A-Z 或 a-z 之一.否则,将 c 视为一个原义的 'c' 字符.
\d	匹配一个数字字符.等价于 [0-9].
\D	匹配一个非数字字符.等价于 [^0-9].
\f	匹配一个换页符.等价于 \x0c 和 \cL.
\n	匹配一个换行符.等价于 \x0a 和 \cJ.
\r	匹配一个回车符.等价于 \x0d 和 \cM.
\s	匹配任何空白字符,包括空格、制表符、换页符等等.等价于 [ \f\n\r\t\v].
\S	匹配任何非空白字符.等价于 [^ \f\n\r\t\v].
\t	匹配一个制表符.等价于 \x09 和 \cI.
\v	匹配一个垂直制表符.等价于 \x0b 和 \cK.
\w	匹配包括下划线的任何单词字符.等价于'[A-Za-z0-9_]'.
\W	匹配任何非单词字符.等价于 '[^A-Za-z0-9_]'.
\xn	匹配 n,其中 n 为十六进制转义值.十六进制转义值必须为确定的两个数字长.
    例如,'\x41' 匹配 "A".'\x041' 则等价于 '\x04' & "1".正则表达式中可以使用 ASCII 编码.
\n	    标识一个八进制转义值或一个向后引用.如果 \n 之前至少 n 个获取的子表达式,则 n 为向后引用.
        否则,如果 n 为八进制数字 (0-7),则 n 为一个八进制转义值.
\nm	    标识一个八进制转义值或一个向后引用.如果 \nm 之前至少有 nm 个获得子表达式,则 nm 为向后引用.
        如果 \nm 之前至少有 n 个获取,则 n 为一个后跟文字 m 的向后引用.
        如果前面的条件都不满足,若 n 和 m 均为八进制数字 (0-7),则 \nm 将匹配八进制转义值 nm.
\nml	如果 n 为八进制数字 (0-3),且 m 和 l 均为八进制数字 (0-7),则匹配八进制转义值 nml.
\un	    匹配 n,其中 n 是一个用四个十六进制数字表示的 Unicode 字符.例如, \u00A9 匹配版权符号.
*/


/* 运算符优先级
\	                        转义符
(), (?:), (?=), []	        圆括号和方括号
*, +, ?, {n}, {n,}, {n,m}	限定符
^, $, \任何元字符, 任何字符	定位点和序列（即：位置和顺序）
|	                        替换,"或"操作
字符具有高于替换运算符的优先级,使得"m|food"匹配"m"或"food".若要匹配"mood"或"food",请使用括号创建子表达式,从而产生"(m|f)ood".
*/

/* 字符簇
[a-z]	    匹配所有的小写字母 
[A-Z]		匹配所有的大写字母 
[a-zA-Z]	匹配所有的字母 
[0-9]		匹配所有的数字 
[0-9\.\-]	匹配所有的数字,句号和减号 
[ \f\r\t\n]	匹配所有的白字符
*/

/*
若要在中括号表达式中包括连字符,请采用下列方法之一：
用反斜扛将它转义：[\-],匹配连字符.
将连字符放在中括号列表的开始或结尾：[-a-z]、[a-z-],匹配所有小写字母和连字符.
创建一个范围,在该范围中,开始字符值小于连字符,而结束字符值等于或大于连字符：[!--]、[!-~].
*/

testStr = "Windows 95, Windows 98, Windows NT"
patt = /Windows (95|98|NT)/g
console.log(patt, testStr.match(patt))
patt = /Windows (?=95|98|NT)/g
console.log(patt, testStr.match(patt))

// ^\s*$	匹配空行
// <\s*(\S+)(\s[^>]*)?>[\s\S]*<\s*\/\1\s*>  匹配 HTML 标记
// 后向引用,表示表达式中,从左往右数,第一个左括号对应的括号内的内容.
// 以此类推,\2表示第二个,\0表示整个表达式.

/* 常用正则表达式
一、校验数字的表达式
数字：^[0-9]*$
n位的数字：^\d{n}$
至少n位的数字：^\d{n,}$
m-n位的数字：^\d{m,n}$
零和非零开头的数字：^(0|[1-9][0-9]*)$
非零开头的最多带两位小数的数字：^([1-9][0-9]*)+(.[0-9]{1,2})?$
带1-2位小数的正数或负数：^(\-)?\d+(\.\d{1,2})?$
正数、负数、和小数：^(\-|\+)?\d+(\.\d+)?$
有两位小数的正实数：^[0-9]+(\.[0-9]{2})?$
有1~3位小数的正实数：^[0-9]+(\.[0-9]{1,3})?$
非零的正整数：^[1-9]\d*$ 或 ^([1-9][0-9]*){1,3}$ 或 ^\+?[1-9][0-9]*$
非零的负整数：^\-[1-9][]0-9"*$ 或 ^-[1-9]\d*$
非负整数：^\d+$ 或 ^[1-9]\d*|0$
非正整数：^-[1-9]\d*|0$ 或 ^((-\d+)|(0+))$
非负浮点数：^\d+(\.\d+)?$ 或 ^[1-9]\d*\.\d*|0\.\d*[1-9]\d*|0?\.0+|0$
非正浮点数：^((-\d+(\.\d+)?)|(0+(\.0+)?))$ 或 ^(-([1-9]\d*\.\d*|0\.\d*[1-9]\d*))|0?\.0+|0$
正浮点数：^[1-9]\d*\.\d*|0\.\d*[1-9]\d*$ 或 ^(([0-9]+\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\.[0-9]+)|([0-9]*[1-9][0-9]*))$
负浮点数：^-([1-9]\d*\.\d*|0\.\d*[1-9]\d*)$ 或 ^(-(([0-9]+\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\.[0-9]+)|([0-9]*[1-9][0-9]*)))$
浮点数：^(-?\d+)(\.\d+)?$ 或 ^-?([1-9]\d*\.\d*|0\.\d*[1-9]\d*|0?\.0+|0)$
二、校验字符的表达式
汉字：^[\u4e00-\u9fa5]{0,}$
英文和数字：^[A-Za-z0-9]+$ 或 ^[A-Za-z0-9]{4,40}$
长度为3-20的所有字符：^.{3,20}$
由26个英文字母组成的字符串：^[A-Za-z]+$
由26个大写英文字母组成的字符串：^[A-Z]+$
由26个小写英文字母组成的字符串：^[a-z]+$
由数字和26个英文字母组成的字符串：^[A-Za-z0-9]+$
由数字、26个英文字母或者下划线组成的字符串：^\w+$ 或 ^\w{3,20}$
中文、英文、数字包括下划线：^[\u4E00-\u9FA5A-Za-z0-9_]+$
中文、英文、数字但不包括下划线等符号：^[\u4E00-\u9FA5A-Za-z0-9]+$ 或 ^[\u4E00-\u9FA5A-Za-z0-9]{2,20}$
可以输入含有^%&',;=?$\"等字符：[^%&',;=?$\x22]+
禁止输入含有~的字符：[^~\x22]+
三、特殊需求表达式
Email地址：^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$
域名：[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(/.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+/.?
InternetURL：[a-zA-z]+://[^\s]* 或 ^http://([\w-]+\.)+[\w-]+(/[\w-./?%&=]*)?$
手机号码：^(13[0-9]|14[5|7]|15[0|1|2|3|5|6|7|8|9]|18[0|1|2|3|5|6|7|8|9])\d{8}$
电话号码("XXX-XXXXXXX"、"XXXX-XXXXXXXX"、"XXX-XXXXXXX"、"XXX-XXXXXXXX"、"XXXXXXX"和"XXXXXXXX)：^(\(\d{3,4}-)|\d{3.4}-)?\d{7,8}$
国内电话号码(0511-4405222、021-87888822)：\d{3}-\d{8}|\d{4}-\d{7}
电话号码正则表达式（支持手机号码,3-4位区号,7-8位直播号码,1－4位分机号）: ((\d{11})|^((\d{7,8})|(\d{4}|\d{3})-(\d{7,8})|(\d{4}|\d{3})-(\d{7,8})-(\d{4}|\d{3}|\d{2}|\d{1})|(\d{7,8})-(\d{4}|\d{3}|\d{2}|\d{1}))$)
身份证号(15位、18位数字),最后一位是校验位,可能为数字或字符X：(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)
帐号是否合法(字母开头,允许5-16字节,允许字母数字下划线)：^[a-zA-Z][a-zA-Z0-9_]{4,15}$
密码(以字母开头,长度在6~18之间,只能包含字母、数字和下划线)：^[a-zA-Z]\w{5,17}$
强密码(必须包含大小写字母和数字的组合,不能使用特殊字符,长度在8-10之间)：^(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,10}$
日期格式：^\d{4}-\d{1,2}-\d{1,2}
一年的12个月(01～09和1～12)：^(0?[1-9]|1[0-2])$
一个月的31天(01～09和1～31)：^((0?[1-9])|((1|2)[0-9])|30|31)$
钱的输入格式：
    有四种钱的表示形式我们可以接受:"10000.00" 和 "10,000.00", 和没有 "分" 的 "10000" 和 "10,000"：^[1-9][0-9]*$
    这表示任意一个不以0开头的数字,但是,这也意味着一个字符"0"不通过,所以我们采用下面的形式：^(0|[1-9][0-9]*)$
    一个0或者一个不以0开头的数字.我们还可以允许开头有一个负号：^(0|-?[1-9][0-9]*)$
    这表示一个0或者一个可能为负的开头不为0的数字.让用户以0开头好了.把负号的也去掉,因为钱总不能是负的吧.下面我们要加的是说明可能的小数部分：^[0-9]+(.[0-9]+)?$
    必须说明的是,小数点后面至少应该有1位数,所以"10."是不通过的,但是 "10" 和 "10.2" 是通过的：^[0-9]+(.[0-9]{2})?$
    这样我们规定小数点后面必须有两位,如果你认为太苛刻了,可以这样：^[0-9]+(.[0-9]{1,2})?$
    这样就允许用户只写一位小数.下面我们该考虑数字中的逗号了,我们可以这样：^[0-9]{1,3}(,[0-9]{3})*(.[0-9]{1,2})?$
    1到3个数字,后面跟着任意个 逗号+3个数字,逗号成为可选,而不是必须：^([0-9]+|[0-9]{1,3}(,[0-9]{3})*)(.[0-9]{1,2})?$
    备注：这就是最终结果了,别忘了"+"可以用"*"替代如果你觉得空字符串也可以接受的话.最后,别忘了在用函数时去掉去掉那个反斜杠,一般的错误都在这里.
xml文件：^([a-zA-Z]+-?)+[a-zA-Z0-9]+\\.[x|X][m|M][l|L]$
中文字符的正则表达式：[\u4e00-\u9fa5]
双字节字符：[^\x00-\xff] (包括汉字在内,可以用来计算字符串的长度(一个双字节字符长度计2,ASCII字符计1))
空白行的正则表达式：\n\s*\r (可以用来删除空白行)
HTML标记的正则表达式：<(\S*?)[^>]*>.*?|<.*? /> ( 首尾空白字符的正则表达式：^\s*|\s*$或(^\s*)|(\s*$) (可以用来删除行首行尾的空白字符(包括空格、制表符、换页符等等),非常有用的表达式)
腾讯QQ号：[1-9][0-9]{4,} (腾讯QQ号从10000开始)
中国邮政编码：[1-9]\d{5}(?!\d) (中国邮政编码为6位数字)
IP地址：((?:(?:25[0-5]|2[0-4]\\d|[01]?\\d?\\d)\\.){3}(?:25[0-5]|2[0-4]\\d|[01]?\\d?\\d))
*/
